﻿#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <math.h>
#include <time.h>
#include "SnakeEatApple.h"
//6.设置控制台光标的位置；
#include <Windows.h>
//34.键盘录入字符awsd；
#include <conio.h>

/*
* 1.定义蛇的结构体；
* 2.初始化蛇和食物；
* 3.开始游戏：
*	蛇和墙的碰撞
*	蛇和自身的碰撞
*	蛇和食物的碰撞
*		重新随机食物
*		蛇身体增长
*		分数增长
*	方向键控制
* 4.游戏结束
*/
void InitFood()
{
	//srand((size_t)time(NULL));
	food[0] = rand() % WIDE;//X坐标为WIDE，宽60；
	food[1] = rand() % HIGH;//Y坐标为HIGH，高20；
}

void InitSnake()
{
	//19.每次蛇头和蛇尾出现的位置都是一样的，不是随机的；头(20,10)尾(19，10)；
	snake.size = 2;//蛇长度为2，1截是蛇头，1截是蛇身；
	snake.body[0].X = WIDE / 2;//蛇头
	snake.body[0].Y = HIGH / 2;

	snake.body[1].X = WIDE / 2 - 1;//蛇身子
	snake.body[1].Y = HIGH / 2;

}

//10.显示界面；
void ShowUI()
{
	COORD coord;
	//42.去掉了system("cls")后，蛇向前走动，尾巴后面的*不会消失，这个时候用' '替代它；
	coord.X = lx;
	coord.Y = ly;
	//43.当蛇向前走了一步后，获取到尾巴的位置光标，在此处输出' '空格，替换掉原来输出的*;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar(' ');

	//11.显示蛇的位置；
	for (int i = 0; i < snake.size; i++)
	{
		coord.X = snake.body[i].X;
		coord.Y = snake.body[i].Y;
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
		if (i == 0)
		{
			//13.蛇头，在光标位置输出一个字符@；（20，10）
			putchar('@');
		}
		else
		{
			//14.长度size为2，是1的话，就是蛇尾了；（19，10）
			putchar('*');
		}

	}

	//12.显示食物的位置；
	coord.X = food[0];
	coord.Y = food[1];
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	//15.在光标处，输出#代表食物，此处也是食物位置；
	putchar('#');

}

void PlayGame()
{
	//29.方向控制，初始值D，即向右；
	char key = 'D';

	//18.对蛇头位置进行约束，不能碰墙；这里的0坐标，是从黑窗口的第一个黑格算起，不是从黑窗口的左上角源点算。
	//所以x=0或y=0时，是第一个数组的下标式的计算，a[0]已经有值了，和数学上0坐标是圆心不一样，这里的0是有值的；
	while (snake.body[0].X >= 0 && snake.body[0].X < WIDE
		&& snake.body[0].Y >= 0 && snake.body[0].Y < HIGH)
	{
		//20.显示界面，蛇头和蛇身，以及食物；
		ShowUI();
		//21.方向控制；就是以蛇头位置为中心，上下左右，增加或减少偏移量；
		//6.getch()函数读入一个字符时,不用 enter 代码会继续跑。
		//而getchar()函数在读入一个字符时，必须按一下 enter 键，该代码才会继续运行。
		//这里是表示程序暂停，必须接收一个字符，才能继续往下执行；
		//35.当键盘输入字符时，才接收；没有的话，不接收，即程序不会卡在接收字符处，即蛇会自动往前走；
		while (_kbhit())
		{
			key = _getch();
		}

		switch (key)
		{
		case 'D':
		case 'd':
			dx = 1; dy = 0;
			break;
		case 'A':
		case 'a':
			dx = -1; dy = 0;
			break;
		case 'W':
		case 'w':
			dx = 0; dy = -1;
			break;
		case 'S':
		case 's':
			dx = 0; dy = 1;
			break;
		}

		//22.是否和自身碰撞；当蛇头body[0]的坐标，等于某个蛇身body[1]位置的坐标时，就是碰撞；
		for (int i = 1; i < snake.size; i++)
		{
			if (snake.body[0].X == snake.body[i].X && snake.body[0].Y == snake.body[i].Y)
			{
				return;
			}
		}
		//23.蛇和食物碰撞；
		if (snake.body[0].X == food[0] && snake.body[0].Y == food[1])
		{
			//24.随机出新食物；
			InitFood();
			//25.蛇身体长度增长；
			snake.size++;
			//26.分数增长；
			score += 10;
			//27.设置关卡：加快速度，障碍物等；
		}
		//28.蛇更新坐标；
		//41.获取到蛇尾巴的坐标；
		lx = snake.body[snake.size - 1].X;
		ly = snake.body[snake.size - 1].Y;
		for (int i = snake.size - 1; i > 0; i--)
		{
			//31.更新蛇身的坐标，就是让后一个蛇身坐标，移动到前一个蛇身坐标；
			snake.body[i].X = snake.body[i - 1].X;
			snake.body[i].Y = snake.body[i - 1].Y;
		}
		//32.更新蛇头的坐标，蛇头坐标加上偏移量，偏移量根据asdw决定；
		snake.body[0].X += dx;
		snake.body[0].Y += dy;

		//33.300毫秒走一次；走完就清屏，为了清除小尾巴；
		Sleep(300);
		//system("cls");
	}
}

void InitWall()
{
	//37.x=60时，输出=；y=20时，输出=；其他时候，都输出' '；外层i控制y，内层j控制x;
	for (int i = 0; i <= HIGH; i++)
	{
		for (int j = 0; j <= WIDE; j++)
		{
			if (i == HIGH)
			{
				putchar('=');
			}
			else if (j == WIDE)
			{
				putchar('=');
			}
			else {
				putchar(' ');
			}
		}
		//38.换行；
		putchar('\n');
	}
}

//main175：案例：贪食蛇游戏
int main175()
{
	//36.去掉控制台输出的食物#_的下滑线，即去掉控制台光标；
	CONSOLE_CURSOR_INFO cci;
	cci.dwSize = sizeof(cci);
	cci.bVisible = FALSE;
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);

	//39.输出墙；
	InitWall();

	//5.输出一下蛇的活动范围；高20，宽60；
	/*for (int i = 0; i < 20; i++)
	{
		for (int j = 0; j < 60; j++)
		{
			printf("*");
		}
		printf("\n");
	}*/
	srand((size_t)time(NULL));
	InitFood();

	//7.根据上面随机出的食物的坐标，设置光标的位置；
	/*
	COORD coord;
	coord.X = food[0];
	coord.Y = food[1];
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);

	//8.设置完控制台光标的位置，就是@的位置。
	printf("@");
	//9.此时食物的坐标即：
	printf("%d   %d", food[0], food[1]);//8   4
	*/
	InitSnake();
	//ShowUI();

	PlayGame();
	//16.程序停一下，其实也不用，因为有下面的system("pause");
	//测试了一下，当蛇撞死后，程序不会立刻结束，需要按一下enter换行，程序才会结束；这里我想让蛇撞死，程序就结束，所以还是去掉；
	//getchar();

	//17.下面这行，会显示：请按任意键继续. . .，所以还是去掉；
	//system("pause");
	return EXIT_SUCCESS;
}
